前面有提到早期 JavaScript 開發者要做到發佈與載入模組沒有一個方便的做法,今天將從 2009 年出現的 CommonJS 社群切入,繼續來聊聊 JavaScript 的模組化歷史,並會介紹一些 Node.js 中使用 CommonJS 模組需要注意的細節。
昨天聊完了在 CommonJS 出現前的各種模組化歷史,雖然沒提到太多細節,今天在繼續考古的過程中發現了一篇文章,Sea.js 作者玉伯 (lifesinger) 曾更簡單明瞭的提過從模組模式到 YUI3 的脈絡,有興趣深入的讀者可以再參考看看。
就在 Ajax、jQuery、YUI3 等技術與工具盛行之時,網頁應用逐漸變得複雜,有一群開發者也正努力想解決當時 JavaScript 在 server side 模組化缺乏一個統一規範的問題,這得從 ServerJS 專案開始說起。
當年 Mozilla 開發者之一的 Kevin Dangoor 在 2009 年初發表了一篇《What Server Side JavaScript needs》,其中講到一些 JavaScript 尚需補足的部份:
綜上所述 Kevin 發起這個稱為 ServerJS 的討論小組想要建立標準與模組化的方式,同年的 8 月時 ServerJS 更名為 CommonJS,展現也想將此標準推向瀏覽器端的野心。並在同年的 JSConf EU 上講了相關的議程。
順帶一提,Ryan Dahl 也在同一場研討會中發表了 Node.js,其中模組化的實作便是直接用上了 CommonJS,可以說反而因此讓 CommonJS 成為最廣為人知的實踐範例。
另外附上一些考古到的討論紀錄:
講了這麼多歷史,實際來看個 Node.js 中 CommonJS 模組的範例:
// module.js
var author = 'codefarmer';
function add(a, b) {
return a + b;
}
module.exports = {
author: author,
add: add
};
// main.js
var myModule = require('./module');
console.log(myModule.author); // codefarmer
console.log(myModule.add(2, 3)); // 5
從上面的這個範例,我們可以透過 module.exports
這個去輸出模組內的變數 、函式、class 等內容,並用 require
在需要的地方去載入模組。
而參考 Node.js 中 CommonJS 模組的文件,以下紀錄一些我有疑慮的注意事項。
exports
或 module.exports
來輸出內容,兩者有什麼差別?// calculator.js
// 寫法一:使用 exports
exports.add = function (a, b) {
return a + b;
};
exports.subtract = function (a, b) {
return a - b;
};
// 寫法二:使用 module.exports
module.exports = {
add: function (a, b) {
return a + b;
},
subtract: function (a, b) {
return a - b;
}
};
兩種輸出方式達到的效果是一樣的,可以理解為當我們在 Node.js 建立一個模組時,Node.js 會為這個模組生成像這樣的空物件,而讓我們可以在模組中去輸出內容 (ref):
var module = { exports: {} };
var exports = module.exports;
但要特別注意的是直接將輸出值指定給 exports 並不會真的改變此模組的輸出,仍會需要用 module.exports
,舉例像是這樣:
// 這不會改變模組的導出,因為此時原本的 module.exports 仍為空物件
exports = { add: function(a, b) {...} };
// 這才會改變模組的導出
module.exports = { add: function(a, b) {...} };
綜上所述,一般來說都還是會傾向直接用 module.exports
來輸出。
require
是同步載入,也就是說在模組載入完成前,程式不會繼續執行require
會緩存載入過的模組,因此多次去載入同一個模組並不會讓這個模組被多次執行關於 Node.js 的歷史雖然很精彩,但跟此系列較無關所以這邊不會贅述,有興趣的話可以參考 Honeypot 幾個月前推出的 Node.js: The Documentary | An origin story 這個高規格製作的 Node.js 紀錄片。
不得不說 Node.js 社群與 Joyent 的 drama 真的是讓這個紀錄片變成一個節奏緊湊精采的 Netflix 電影的感覺,紀錄一些影片中會提到的部分:
今天又不小心挖得太深入,原本差點還要來寫 Node.js 的紀錄片筆記,但如上述較無關所以最後只保留了一小段,明天將會聊聊 CommonJS 社群對於走向 client side 模組標準化的意見分歧,而產生了 AMD、CMD、UMD 的百家爭鳴的故事。